﻿using System;
using System.Collections.Generic;

namespace Core
{
    public interface IDispose
    {
        void Dispose();
    }

    /// <summary>
    /// 引用池。
    /// </summary>
    public static partial class ReferencePool
    {
        private static readonly Dictionary<Type, Group> s_dict = new();

        /// <summary>
        /// 获取或设置是否开启强制检查。
        /// </summary>
        public static bool EnableStrictCheck { get; set; } = false;

        /// <summary>
        /// 获取引用池的数量。
        /// </summary>
        public static int Count
        {
            get
            {
                lock (s_dict)
                {
                    return s_dict.Count;
                }
            }
        }

        /// <summary>
        /// 清除所有引用池。
        /// </summary>
        public static void Clear()
        {
            lock (s_dict)
            {
                foreach (var pair in s_dict)
                {
                    pair.Value.RemoveAll();
                }

                s_dict.Clear();
            }
        }

        /// <summary>
        /// 从引用池获取引用。
        /// </summary>
        /// <typeparam name="T">引用类型。</typeparam>
        /// <returns>引用。</returns>
        public static T Acquire<T>() where T : class, IDispose
        {
            return GetReferenceCollection(typeof(T)).Pop<T>();
        }

        /// <summary>
        /// 从引用池获取引用。
        /// </summary>
        /// <param name="t">引用类型。</param>
        /// <returns>引用。</returns>
        public static IDispose Acquire(Type t)
        {
            InternalCheckReferenceType(t);
            return GetReferenceCollection(t).Pop();
        }

        /// <summary>
        /// 将引用归还引用池。
        /// </summary>
        /// <param name="obj">引用。</param>
        public static void Recycle(IDispose obj)
        {
            if (obj == null)
            {
                throw new Exception("Reference is invalid.");
            }

            Type t = obj.GetType();
            InternalCheckReferenceType(t);
            GetReferenceCollection(t).Push(obj);
        }


        private static void InternalCheckReferenceType(Type t)
        {
            if (!EnableStrictCheck)
            {
                return;
            }

            if (t == null)
            {
                throw new Exception("Reference type is invalid.");
            }

            if (!t.IsClass || t.IsAbstract)
            {
                throw new Exception("Reference type is not a non-abstract class type.");
            }

            if (!typeof(IDispose).IsAssignableFrom(t))
            {
                throw new Exception($"Reference type '{t.FullName}' is invalid.");
            }
        }

        private static Group GetReferenceCollection(Type t)
        {
            if (t == null)
            {
                throw new Exception("ReferenceType is invalid.");
            }

            Group item;
            lock (s_dict)
            {
                if (s_dict.TryGetValue(t, out item)) return item;
                item = new Group(t);
                s_dict.Add(t, item);
            }

            return item;
        }
    }
}
